{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.0.0-beta1\n",
      "sys.version_info(major=3, minor=5, micro=3, releaselevel='final', serial=0)\n",
      "matplotlib 3.0.3\n",
      "numpy 1.16.4\n",
      "pandas 0.24.2\n",
      "sklearn 0.21.2\n",
      "tensorflow 2.0.0-beta1\n",
      "tensorflow.python.keras.api._v2.keras 2.2.4-tf\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "import tensorflow as tf\n",
    "\n",
    "from tensorflow import keras\n",
    "\n",
    "print(tf.__version__)\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, tf, keras:\n",
    "    print(module.__name__, module.__version__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1. loads data\n",
    "# 2. preprocesses data -> dataset\n",
    "# 3. tools\n",
    "# 3.1 generates position embedding\n",
    "# 3.2 create mask. (a. padding, b. decoder)\n",
    "# 3.3 scaled_dot_product_attention\n",
    "# 4. builds model\n",
    "# 4.1 MultiheadAttention\n",
    "# 4.2 EncoderLayer\n",
    "# 4.3 DecoderLayer\n",
    "# 4.4 EncoderModel\n",
    "# 4.5 DecoderModel\n",
    "# 4.6 Transformer\n",
    "# 5. optimizer & loss\n",
    "# 6. train step -> train\n",
    "# 7. Evaluate and Visualize"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tfds.core.DatasetInfo(\n",
      "    name='ted_hrlr_translate',\n",
      "    version=0.0.1,\n",
      "    description='Data sets derived from TED talk transcripts for comparing similar language pairs\n",
      "where one is high resource and the other is low resource.\n",
      "',\n",
      "    urls=['https://github.com/neulab/word-embeddings-for-nmt'],\n",
      "    features=Translation({\n",
      "        'en': Text(shape=(), dtype=tf.string, encoder=None),\n",
      "        'pt': Text(shape=(), dtype=tf.string, encoder=None)\n",
      "    },\n",
      "    total_num_examples=54781,\n",
      "    splits={\n",
      "        'test': <tfds.core.SplitInfo num_examples=1803>,\n",
      "        'train': <tfds.core.SplitInfo num_examples=51785>,\n",
      "        'validation': <tfds.core.SplitInfo num_examples=1193>\n",
      "    },\n",
      "    supervised_keys=('pt', 'en'),\n",
      "    citation='\"\"\"\n",
      "        @inproceedings{Ye2018WordEmbeddings,\n",
      "          author  = {Ye, Qi and Devendra, Sachan and Matthieu, Felix and Sarguna, Padmanabhan and Graham, Neubig},\n",
      "          title   = {When and Why are pre-trained word embeddings useful for Neural Machine Translation},\n",
      "          booktitle = {HLT-NAACL},\n",
      "          year    = {2018},\n",
      "          }\n",
      "        \n",
      "    \"\"\"',\n",
      "    redistribution_info=,\n",
      ")\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import tensorflow_datasets as tfds\n",
    "\n",
    "examples, info = tfds.load('ted_hrlr_translate/pt_to_en',\n",
    "                           with_info = True,\n",
    "                           as_supervised = True)\n",
    "\n",
    "train_examples, val_examples = examples['train'], examples['validation']\n",
    "print(info)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'o problema \\xc3\\xa9 que nunca vivi l\\xc3\\xa1 um \\xc3\\xbanico dia .'\n",
      "b\"except , i 've never lived one day of my life there .\"\n",
      "\n",
      "b'os astr\\xc3\\xb3nomos acreditam que cada estrela da gal\\xc3\\xa1xia tem um planeta , e especulam que at\\xc3\\xa9 um quinto deles tem um planeta do tipo da terra que poder\\xc3\\xa1 ter vida , mas ainda n\\xc3\\xa3o vimos nenhum deles .'\n",
      "b\"astronomers now believe that every star in the galaxy has a planet , and they speculate that up to one fifth of them have an earth-like planet that might be able to harbor life , but we have n't seen any of them .\"\n",
      "\n",
      "b'agora aqui temos imagens sendo extra\\xc3\\xaddas em tempo real diretamente do feed ,'\n",
      "b'now here are live images being pulled straight from the feed .'\n",
      "\n",
      "b'agora : um , dois , tr\\xc3\\xaas , vai .'\n",
      "b'so : one , two , three , go .'\n",
      "\n",
      "b'eventualmente , vamos ver se teremos todos os sentidos humanos empregues , e se vamos ter meios para viver a hist\\xc3\\xb3ria qualquer que seja a via escolhida .'\n",
      "b'eventually , we can see if we will have all of our human senses employed , and we will have agency to live the story in any path we choose .'\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for pt, en in train_examples.take(5):\n",
    "    print(pt.numpy())\n",
    "    print(en.numpy())\n",
    "    print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "en_tokenizer = tfds.features.text.SubwordTextEncoder.build_from_corpus(\n",
    "    (en.numpy() for pt, en in train_examples),\n",
    "    target_vocab_size = 2 ** 13)\n",
    "pt_tokenizer = tfds.features.text.SubwordTextEncoder.build_from_corpus(\n",
    "    (pt.numpy() for pt, en in train_examples),\n",
    "    target_vocab_size = 2 ** 13)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tokenized string is [7915, 1248, 7946, 7194, 13, 2799, 7877]\n",
      "The original string is Transformer is awesome.\n",
      "7915 --> \"T\"\n",
      "1248 --> \"ran\"\n",
      "7946 --> \"s\"\n",
      "7194 --> \"former \"\n",
      "13 --> \"is \"\n",
      "2799 --> \"awesome\"\n",
      "7877 --> \".\"\n"
     ]
    }
   ],
   "source": [
    "sample_string = \"Transformer is awesome.\"\n",
    "\n",
    "tokenized_string = en_tokenizer.encode(sample_string)\n",
    "print('Tokenized string is {}'.format(tokenized_string))\n",
    "\n",
    "origin_string = en_tokenizer.decode(tokenized_string)\n",
    "print('The original string is {}'.format(origin_string))\n",
    "\n",
    "assert origin_string == sample_string\n",
    "\n",
    "for token in tokenized_string:\n",
    "    print('{} --> \"{}\"'.format(token, en_tokenizer.decode([token])))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "buffer_size = 20000\n",
    "batch_size = 64\n",
    "max_length = 40\n",
    "\n",
    "def encode_to_subword(pt_sentence, en_sentence):\n",
    "    pt_sequence = [pt_tokenizer.vocab_size] \\\n",
    "    + pt_tokenizer.encode(pt_sentence.numpy()) \\\n",
    "    + [pt_tokenizer.vocab_size + 1]\n",
    "    en_sequence = [en_tokenizer.vocab_size] \\\n",
    "    + en_tokenizer.encode(en_sentence.numpy()) \\\n",
    "    + [en_tokenizer.vocab_size + 1]\n",
    "    return pt_sequence, en_sequence\n",
    "\n",
    "def filter_by_max_length(pt, en):\n",
    "    return tf.logical_and(tf.size(pt) <= max_length,\n",
    "                          tf.size(en) <= max_length)\n",
    "\n",
    "def tf_encode_to_subword(pt_sentence, en_sentence):\n",
    "    return tf.py_function(encode_to_subword,\n",
    "                          [pt_sentence, en_sentence],\n",
    "                          [tf.int64, tf.int64])\n",
    "train_dataset = train_examples.map(tf_encode_to_subword)\n",
    "train_dataset = train_dataset.filter(filter_by_max_length)\n",
    "train_dataset = train_dataset.shuffle(\n",
    "    buffer_size).padded_batch(\n",
    "    batch_size, padded_shapes=([-1], [-1]))\n",
    "\n",
    "valid_dataset = val_examples.map(tf_encode_to_subword)\n",
    "valid_dataset = valid_dataset.filter(\n",
    "    filter_by_max_length).padded_batch(\n",
    "    batch_size, padded_shapes=([-1], [-1]))\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING: Logging before flag parsing goes to stderr.\n",
      "W0804 07:09:00.523236 140247652361984 backprop.py:842] The dtype of the watched tensor must be floating (e.g. tf.float32), got tf.string\n",
      "W0804 07:09:00.525246 140247652361984 backprop.py:842] The dtype of the watched tensor must be floating (e.g. tf.float32), got tf.string\n",
      "W0804 07:09:00.527645 140247761401600 backprop.py:842] The dtype of the watched tensor must be floating (e.g. tf.float32), got tf.string\n",
      "W0804 07:09:00.528782 140247761401600 backprop.py:842] The dtype of the watched tensor must be floating (e.g. tf.float32), got tf.string\n",
      "W0804 07:09:00.531126 140247744616192 backprop.py:842] The dtype of the watched tensor must be floating (e.g. tf.float32), got tf.string\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(64, 40) (64, 40)\n",
      "(64, 38) (64, 40)\n",
      "(64, 40) (64, 40)\n",
      "(64, 39) (64, 39)\n",
      "(64, 37) (64, 38)\n"
     ]
    }
   ],
   "source": [
    "for pt_batch, en_batch in valid_dataset.take(5):\n",
    "    print(pt_batch.shape, en_batch.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 50, 512)\n"
     ]
    }
   ],
   "source": [
    "# PE(pos, 2i)   = sin(pos / 10000^(2i/d_model))\n",
    "# PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))\n",
    "\n",
    "# pos.shape: [sentence_length, 1]\n",
    "# i.shape  : [1, d_model]\n",
    "# result.shape: [sentence_length, d_model]\n",
    "def get_angles(pos, i, d_model):\n",
    "    angle_rates = 1 / np.power(10000,\n",
    "                               (2 * (i // 2)) / np.float32(d_model))\n",
    "    return pos * angle_rates\n",
    "\n",
    "def get_position_embedding(sentence_length, d_model):\n",
    "    angle_rads = get_angles(np.arange(sentence_length)[:, np.newaxis],\n",
    "                            np.arange(d_model)[np.newaxis, :],\n",
    "                            d_model)\n",
    "    # sines.shape: [sentence_length, d_model / 2]\n",
    "    # cosines.shape: [sentence_length, d_model / 2]\n",
    "    sines = np.sin(angle_rads[:, 0::2])\n",
    "    cosines = np.cos(angle_rads[:, 1::2])\n",
    "    \n",
    "    # position_embedding.shape: [sentence_length, d_model]\n",
    "    position_embedding = np.concatenate([sines, cosines], axis = -1)\n",
    "    # position_embedding.shape: [1, sentence_length, d_model]\n",
    "    position_embedding = position_embedding[np.newaxis, ...]\n",
    "    \n",
    "    return tf.cast(position_embedding, dtype=tf.float32)\n",
    "\n",
    "position_embedding = get_position_embedding(50, 512)\n",
    "print(position_embedding.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "def plot_position_embedding(position_embedding):\n",
    "    plt.pcolormesh(position_embedding[0], cmap = 'RdBu')\n",
    "    plt.xlabel('Depth')\n",
    "    plt.xlim((0, 512))\n",
    "    plt.ylabel('Position')\n",
    "    plt.colorbar()\n",
    "    plt.show()\n",
    "    \n",
    "plot_position_embedding(position_embedding)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: id=313180, shape=(3, 1, 1, 5), dtype=float32, numpy=\n",
       "array([[[[0., 0., 1., 1., 0.]]],\n",
       "\n",
       "\n",
       "       [[[0., 0., 0., 1., 1.]]],\n",
       "\n",
       "\n",
       "       [[[1., 1., 1., 0., 0.]]]], dtype=float32)>"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 1. padding mask, 2. look ahead\n",
    "\n",
    "# batch_data.shape: [batch_size, seq_len]\n",
    "def create_padding_mask(batch_data):\n",
    "    padding_mask = tf.cast(tf.math.equal(batch_data, 0), tf.float32)\n",
    "    # [batch_size, 1, 1, seq_len]\n",
    "    return padding_mask[:, tf.newaxis, tf.newaxis, :]\n",
    "\n",
    "x = tf.constant([[7, 6, 0, 0, 1], [1, 2, 3, 0, 0], [0, 0, 0, 4, 5]])\n",
    "create_padding_mask(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: id=313189, shape=(3, 3), dtype=float32, numpy=\n",
       "array([[0., 1., 1.],\n",
       "       [0., 0., 1.],\n",
       "       [0., 0., 0.]], dtype=float32)>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# attention_weights.shape: [3,3]\n",
    "# [[1, 0, 0],\n",
    "#  [4, 5, 0],\n",
    "#  [7, 8, 9]]\n",
    "def create_look_ahead_mask(size):\n",
    "    mask = 1 - tf.linalg.band_part(tf.ones((size, size)), -1, 0)\n",
    "    return mask # (seq_len, seq_len)\n",
    "\n",
    "create_look_ahead_mask(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def scaled_dot_product_attention(q, k, v, mask):\n",
    "    \"\"\"\n",
    "    Args:\n",
    "    - q: shape == (..., seq_len_q, depth)\n",
    "    - k: shape == (..., seq_len_k, depth)\n",
    "    - v: shape == (..., seq_len_v, depth_v)\n",
    "    - seq_len_k == seq_len_v\n",
    "    - mask: shape == (..., seq_len_q, seq_len_k)\n",
    "    Returns:\n",
    "    - output: weighted sum\n",
    "    - attention_weights: weights of attention\n",
    "    \"\"\"\n",
    "    \n",
    "    # matmul_qk.shape: (..., seq_len_q, seq_len_k)\n",
    "    matmul_qk = tf.matmul(q, k, transpose_b = True)\n",
    "    \n",
    "    dk = tf.cast(tf.shape(k)[-1], tf.float32)\n",
    "    scaled_attention_logits = matmul_qk / tf.math.sqrt(dk)\n",
    "    \n",
    "    if mask is not None:\n",
    "        # 使得在softmax后值趋近于0\n",
    "        scaled_attention_logits += (mask * -1e9)\n",
    "    \n",
    "    # attention_weights.shape: (..., seq_len_q, seq_len_k)\n",
    "    attention_weights = tf.nn.softmax(\n",
    "        scaled_attention_logits, axis = -1)\n",
    "    \n",
    "    # output.shape: (..., seq_len_q, depth_v)\n",
    "    output = tf.matmul(attention_weights, v)\n",
    "    \n",
    "    return output, attention_weights\n",
    "\n",
    "def print_scaled_dot_product_attention(q, k, v):\n",
    "    temp_out, temp_att = scaled_dot_product_attention(q, k, v, None)\n",
    "    print(\"Attention weights are:\")\n",
    "    print(temp_att)\n",
    "    print(\"Output is:\")\n",
    "    print(temp_out)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Attention weights are:\n",
      "tf.Tensor([[0. 1. 0. 0.]], shape=(1, 4), dtype=float32)\n",
      "Output is:\n",
      "tf.Tensor([[10.  0.]], shape=(1, 2), dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "temp_k = tf.constant([[10, 0, 0],\n",
    "                      [0, 10, 0],\n",
    "                      [0, 0, 10],\n",
    "                      [0, 0, 10]], dtype=tf.float32) # (4, 3)\n",
    "\n",
    "temp_v = tf.constant([[1, 0],\n",
    "                      [10, 0],\n",
    "                      [100, 5],\n",
    "                      [1000, 6]], dtype=tf.float32) # (4, 2)\n",
    "\n",
    "temp_q1 = tf.constant([[0, 10, 0]], dtype=tf.float32) # (1, 3)\n",
    "np.set_printoptions(suppress=True)\n",
    "print_scaled_dot_product_attention(temp_q1, temp_k, temp_v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Attention weights are:\n",
      "tf.Tensor([[0.  0.  0.5 0.5]], shape=(1, 4), dtype=float32)\n",
      "Output is:\n",
      "tf.Tensor([[550.    5.5]], shape=(1, 2), dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "temp_q2 = tf.constant([[0, 0, 10]], dtype=tf.float32) # (1, 3)\n",
    "print_scaled_dot_product_attention(temp_q2, temp_k, temp_v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Attention weights are:\n",
      "tf.Tensor([[0.5 0.5 0.  0. ]], shape=(1, 4), dtype=float32)\n",
      "Output is:\n",
      "tf.Tensor([[5.5 0. ]], shape=(1, 2), dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "temp_q3 = tf.constant([[10, 10, 0]], dtype=tf.float32) # (1, 3)\n",
    "print_scaled_dot_product_attention(temp_q3, temp_k, temp_v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Attention weights are:\n",
      "tf.Tensor(\n",
      "[[0.  1.  0.  0. ]\n",
      " [0.  0.  0.5 0.5]\n",
      " [0.5 0.5 0.  0. ]], shape=(3, 4), dtype=float32)\n",
      "Output is:\n",
      "tf.Tensor(\n",
      "[[ 10.    0. ]\n",
      " [550.    5.5]\n",
      " [  5.5   0. ]], shape=(3, 2), dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "temp_q4 = tf.constant([[0, 10, 0],\n",
    "                       [0, 0, 10],\n",
    "                       [10, 10, 0]], dtype=tf.float32) # (3, 3)\n",
    "print_scaled_dot_product_attention(temp_q4, temp_k, temp_v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 60, 512)\n",
      "(1, 8, 60, 60)\n"
     ]
    }
   ],
   "source": [
    "class MultiHeadAttention(keras.layers.Layer):\n",
    "    \"\"\"\n",
    "    理论上:\n",
    "    x -> Wq0 -> q0\n",
    "    x -> Wk0 -> k0\n",
    "    x -> Wv0 -> v0\n",
    "    \n",
    "    实战中:\n",
    "    q -> Wq0 -> q0\n",
    "    k -> Wk0 -> k0\n",
    "    v -> Wv0 -> v0\n",
    "    \n",
    "    实战中技巧：\n",
    "    q -> Wq -> Q -> split -> q0, q1, q2...\n",
    "    \"\"\"\n",
    "    def __init__(self, d_model, num_heads):\n",
    "        super(MultiHeadAttention, self).__init__()\n",
    "        self.num_heads = num_heads\n",
    "        self.d_model = d_model\n",
    "        assert self.d_model % self.num_heads == 0\n",
    "        \n",
    "        self.depth = self.d_model // self.num_heads\n",
    "        \n",
    "        self.WQ = keras.layers.Dense(self.d_model)\n",
    "        self.WK = keras.layers.Dense(self.d_model)\n",
    "        self.WV = keras.layers.Dense(self.d_model)\n",
    "        \n",
    "        self.dense = keras.layers.Dense(self.d_model)\n",
    "    \n",
    "    def split_heads(self, x, batch_size):\n",
    "        # x.shape: (batch_size, seq_len, d_model)\n",
    "        # d_model = num_heads * depth\n",
    "        # x -> (batch_size, num_heads, seq_len, depth)\n",
    "        \n",
    "        x = tf.reshape(x,\n",
    "                       (batch_size, -1, self.num_heads, self.depth))\n",
    "        return tf.transpose(x, perm=[0, 2, 1, 3])\n",
    "    \n",
    "    def call(self, q, k, v, mask):\n",
    "        batch_size = tf.shape(q)[0]\n",
    "        \n",
    "        q = self.WQ(q) # q.shape: (batch_size, seq_len_q, d_model)\n",
    "        k = self.WK(k) # k.shape: (batch_size, seq_len_k, d_model)\n",
    "        v = self.WV(v) # v.shape: (batch_size, seq_len_v, d_model)\n",
    "        \n",
    "        # q.shape: (batch_size, num_heads, seq_len_q, depth)\n",
    "        q = self.split_heads(q, batch_size)\n",
    "        # k.shape: (batch_size, num_heads, seq_len_k, depth)\n",
    "        k = self.split_heads(k, batch_size)\n",
    "        # v.shape: (batch_size, num_heads, seq_len_v, depth)\n",
    "        v = self.split_heads(v, batch_size)\n",
    "        \n",
    "        # scaled_attention_outputs.shape: (batch_size, num_heads, seq_len_q, depth)\n",
    "        # attention_weights.shape: (batch_size, num_heads, seq_len_q, seq_len_k)\n",
    "        scaled_attention_outputs, attention_weights = \\\n",
    "        scaled_dot_product_attention(q, k, v, mask)\n",
    "        \n",
    "        # scaled_attention_outputs.shape: (batch_size, seq_len_q, num_heads, depth)\n",
    "        scaled_attention_outputs = tf.transpose(\n",
    "            scaled_attention_outputs, perm = [0, 2, 1, 3])\n",
    "        # concat_attention.shape: (batch_size, seq_len_q, d_model)\n",
    "        concat_attention = tf.reshape(scaled_attention_outputs,\n",
    "                                      (batch_size, -1, self.d_model))\n",
    "        \n",
    "        # output.shape : (batch_size, seq_len_q, d_model)\n",
    "        output = self.dense(concat_attention)\n",
    "        \n",
    "        return output, attention_weights\n",
    "    \n",
    "temp_mha = MultiHeadAttention(d_model=512, num_heads=8)\n",
    "y = tf.random.uniform((1, 60, 256)) # (batch_size, seq_len_q, dim)\n",
    "output, attn = temp_mha(y, y, y, mask = None)\n",
    "print(output.shape)\n",
    "print(attn.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "TensorShape([64, 50, 512])"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def feed_forward_network(d_model, dff):\n",
    "    # dff: dim of feed forward network.\n",
    "    return keras.Sequential([\n",
    "        keras.layers.Dense(dff, activation='relu'),\n",
    "        keras.layers.Dense(d_model)\n",
    "    ])\n",
    "\n",
    "sample_ffn = feed_forward_network(512, 2048)\n",
    "sample_ffn(tf.random.uniform((64, 50, 512))).shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(64, 50, 512)\n"
     ]
    }
   ],
   "source": [
    "class EncoderLayer(keras.layers.Layer):\n",
    "    \"\"\"\n",
    "    x -> self attention -> add & normalize & dropout\n",
    "      -> feed_forward -> add & normalize & dropout\n",
    "    \"\"\"\n",
    "    def __init__(self, d_model, num_heads, dff, rate=0.1):\n",
    "        super(EncoderLayer, self).__init__()\n",
    "        self.mha = MultiHeadAttention(d_model, num_heads)\n",
    "        self.ffn = feed_forward_network(d_model, dff)\n",
    "        \n",
    "        self.layer_norm1 = keras.layers.LayerNormalization(\n",
    "            epsilon = 1e-6)\n",
    "        self.layer_norm2 = keras.layers.LayerNormalization(\n",
    "            epsilon = 1e-6)\n",
    "        \n",
    "        self.dropout1 = keras.layers.Dropout(rate)\n",
    "        self.dropout2 = keras.layers.Dropout(rate)\n",
    "    \n",
    "    def call(self, x, training, encoder_padding_mask):\n",
    "        # x.shape          : (batch_size, seq_len, dim=d_model)\n",
    "        # attn_output.shape: (batch_size, seq_len, d_model)\n",
    "        # out1.shape       : (batch_size, seq_len, d_model)\n",
    "        attn_output, _ = self.mha(x, x, x, encoder_padding_mask)\n",
    "        attn_output = self.dropout1(attn_output, training=training)\n",
    "        out1 = self.layer_norm1(x + attn_output)\n",
    "        \n",
    "        # ffn_output.shape: (batch_size, seq_len, d_model)\n",
    "        # out2.shape      : (batch_size, seq_len, d_model)\n",
    "        ffn_output = self.ffn(out1)\n",
    "        ffn_output = self.dropout2(ffn_output, training=training)\n",
    "        out2 = self.layer_norm2(out1 + ffn_output)\n",
    "        \n",
    "        return out2\n",
    "\n",
    "sample_encoder_layer = EncoderLayer(512, 8, 2048)\n",
    "sample_input = tf.random.uniform((64, 50, 512))\n",
    "sample_output = sample_encoder_layer(sample_input, False, None)\n",
    "print(sample_output.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(64, 60, 512)\n",
      "(64, 8, 60, 60)\n",
      "(64, 8, 60, 50)\n"
     ]
    }
   ],
   "source": [
    "class DecoderLayer(keras.layers.Layer):\n",
    "    \"\"\"\n",
    "    x -> self attention -> add & normalize & dropout -> out1\n",
    "    out1 , encoding_outputs -> attention -> add & normalize & dropout -> out2\n",
    "    out2 -> ffn -> add & normalize & dropout -> out3\n",
    "    \"\"\"\n",
    "    def __init__(self, d_model, num_heads, dff, rate = 0.1):\n",
    "        super(DecoderLayer, self).__init__()\n",
    "        \n",
    "        self.mha1 = MultiHeadAttention(d_model, num_heads)\n",
    "        self.mha2 = MultiHeadAttention(d_model, num_heads)\n",
    "        \n",
    "        self.ffn = feed_forward_network(d_model, dff)\n",
    "        \n",
    "        self.layer_norm1 = keras.layers.LayerNormalization(\n",
    "            epsilon = 1e-6)\n",
    "        self.layer_norm2 = keras.layers.LayerNormalization(\n",
    "            epsilon = 1e-6)\n",
    "        self.layer_norm3 = keras.layers.LayerNormalization(\n",
    "            epsilon = 1e-6)\n",
    "        \n",
    "        self.dropout1 = keras.layers.Dropout(rate)\n",
    "        self.dropout2 = keras.layers.Dropout(rate)\n",
    "        self.dropout3 = keras.layers.Dropout(rate)\n",
    "    \n",
    "    \n",
    "    def call(self, x, encoding_outputs, training,\n",
    "             decoder_mask, encoder_decoder_padding_mask):\n",
    "        # decoder_mask: 由look_ahead_mask和decoder_padding_mask合并而来\n",
    "        \n",
    "        # x.shape: (batch_size, target_seq_len, d_model)\n",
    "        # encoding_outputs.shape: (batch_size, input_seq_len, d_model)\n",
    "        \n",
    "        # attn1, out1.shape : (batch_size, target_seq_len, d_model)\n",
    "        attn1, attn_weights1 = self.mha1(x, x, x, decoder_mask)\n",
    "        attn1 = self.dropout1(attn1, training = training)\n",
    "        out1 = self.layer_norm1(attn1 + x)\n",
    "        \n",
    "        # attn2, out2.shape : (batch_size, target_seq_len, d_model)\n",
    "        attn2, attn_weights2 = self.mha2(\n",
    "            out1, encoding_outputs, encoding_outputs,\n",
    "            encoder_decoder_padding_mask)\n",
    "        attn2 = self.dropout2(attn2, training = training)\n",
    "        out2 = self.layer_norm2(attn2 + out1)\n",
    "        \n",
    "        # ffn_output, out3.shape: (batch_size, target_seq_len, d_model)\n",
    "        ffn_output = self.ffn(out2)\n",
    "        ffn_output = self.dropout3(ffn_output, training=training)\n",
    "        out3 = self.layer_norm3(ffn_output + out2)\n",
    "        \n",
    "        return out3, attn_weights1, attn_weights2\n",
    "    \n",
    "sample_decoder_layer = DecoderLayer(512, 8, 2048)\n",
    "sample_decoder_input = tf.random.uniform((64, 60, 512))\n",
    "sample_decoder_output, sample_decoder_attn_weights1, sample_decoder_attn_weights2 = sample_decoder_layer(\n",
    "    sample_decoder_input, sample_output, False, None, None)\n",
    "\n",
    "print(sample_decoder_output.shape)\n",
    "print(sample_decoder_attn_weights1.shape)\n",
    "print(sample_decoder_attn_weights2.shape)\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(64, 37, 512)\n"
     ]
    }
   ],
   "source": [
    "class EncoderModel(keras.layers.Layer):\n",
    "    def __init__(self, num_layers, input_vocab_size, max_length,\n",
    "                 d_model, num_heads, dff, rate=0.1):\n",
    "        super(EncoderModel, self).__init__()\n",
    "        self.d_model = d_model\n",
    "        self.num_layers = num_layers\n",
    "        self.max_length = max_length\n",
    "        \n",
    "        self.embedding = keras.layers.Embedding(input_vocab_size,\n",
    "                                                self.d_model)\n",
    "        # position_embedding.shape: (1, max_length, d_model)\n",
    "        self.position_embedding = get_position_embedding(max_length,\n",
    "                                                         self.d_model)\n",
    "        \n",
    "        self.dropout = keras.layers.Dropout(rate)\n",
    "        self.encoder_layers = [\n",
    "            EncoderLayer(d_model, num_heads, dff, rate)\n",
    "            for _ in range(self.num_layers)]\n",
    "        \n",
    "    \n",
    "    def call(self, x, training, encoder_padding_mask):\n",
    "        # x.shape: (batch_size, input_seq_len)\n",
    "        input_seq_len = tf.shape(x)[1]\n",
    "        tf.debugging.assert_less_equal(\n",
    "            input_seq_len, self.max_length,\n",
    "            \"input_seq_len should be less or equal to self.max_length\")\n",
    "        \n",
    "        # x.shape: (batch_size, input_seq_len, d_model)\n",
    "        x = self.embedding(x)\n",
    "        x *= tf.math.sqrt(tf.cast(self.d_model, tf.float32))\n",
    "        x += self.position_embedding[:, :input_seq_len, :]\n",
    "        \n",
    "        x = self.dropout(x, training = training)\n",
    "        \n",
    "        for i in range(self.num_layers):\n",
    "            x = self.encoder_layers[i](x, training,\n",
    "                                       encoder_padding_mask)\n",
    "        \n",
    "        # x.shape: (batch_size, input_seq_len, d_model)\n",
    "        return x\n",
    "    \n",
    "sample_encoder_model = EncoderModel(2, 8500, max_length,\n",
    "                                    512, 8, 2048)\n",
    "sample_encoder_model_input = tf.random.uniform((64, 37))\n",
    "sample_encoder_model_output = sample_encoder_model(\n",
    "    sample_encoder_model_input, False, encoder_padding_mask = None)\n",
    "print(sample_encoder_model_output.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(64, 35, 512)\n",
      "(64, 8, 35, 37)\n",
      "(64, 8, 35, 35)\n",
      "(64, 8, 35, 37)\n",
      "(64, 8, 35, 35)\n"
     ]
    }
   ],
   "source": [
    "class DecoderModel(keras.layers.Layer):\n",
    "    def __init__(self, num_layers, target_vocab_size, max_length,\n",
    "                 d_model, num_heads, dff, rate=0.1):\n",
    "        super(DecoderModel, self).__init__()\n",
    "        self.num_layers = num_layers\n",
    "        self.max_length = max_length\n",
    "        self.d_model = d_model\n",
    "        \n",
    "        self.embedding = keras.layers.Embedding(target_vocab_size,\n",
    "                                                d_model)\n",
    "        self.position_embedding = get_position_embedding(max_length,\n",
    "                                                         d_model)\n",
    "        \n",
    "        self.dropout = keras.layers.Dropout(rate)\n",
    "        self.decoder_layers = [\n",
    "            DecoderLayer(d_model, num_heads, dff, rate)\n",
    "            for _ in range(self.num_layers)]\n",
    "        \n",
    "    \n",
    "    def call(self, x, encoding_outputs, training,\n",
    "             decoder_mask, encoder_decoder_padding_mask):\n",
    "        # x.shape: (batch_size, output_seq_len)\n",
    "        output_seq_len = tf.shape(x)[1]\n",
    "        tf.debugging.assert_less_equal(\n",
    "            output_seq_len, self.max_length,\n",
    "            \"output_seq_len should be less or equal to self.max_length\")\n",
    "        \n",
    "        attention_weights = {}\n",
    "        \n",
    "        # x.shape: (batch_size, output_seq_len, d_model)\n",
    "        x = self.embedding(x)\n",
    "        x *= tf.math.sqrt(tf.cast(self.d_model, tf.float32))\n",
    "        x += self.position_embedding[:, :output_seq_len, :]\n",
    "        \n",
    "        x = self.dropout(x, training = training)\n",
    "        \n",
    "        for i in range(self.num_layers):\n",
    "            x, attn1, attn2 = self.decoder_layers[i](\n",
    "                x, encoding_outputs, training,\n",
    "                decoder_mask, encoder_decoder_padding_mask)\n",
    "            attention_weights[\n",
    "                'decoder_layer{}_att1'.format(i+1)] = attn1\n",
    "            attention_weights[\n",
    "                'decoder_layer{}_att2'.format(i+1)] = attn2\n",
    "        # x.shape: (batch_size, output_seq_len, d_model)\n",
    "        return x, attention_weights\n",
    "\n",
    "sample_decoder_model = DecoderModel(2, 8000, max_length,\n",
    "                                    512, 8, 2048)\n",
    "\n",
    "sample_decoder_model_input = tf.random.uniform((64, 35))\n",
    "sample_decoder_model_output, sample_decoder_model_att \\\n",
    "= sample_decoder_model(\n",
    "    sample_decoder_model_input,\n",
    "    sample_encoder_model_output,\n",
    "    training = False, decoder_mask = None,\n",
    "    encoder_decoder_padding_mask = None)\n",
    "\n",
    "print(sample_decoder_model_output.shape)\n",
    "for key in sample_decoder_model_att:\n",
    "    print(sample_decoder_model_att[key].shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(64, 31, 8000)\n",
      "decoder_layer1_att2 (64, 8, 31, 26)\n",
      "decoder_layer2_att1 (64, 8, 31, 31)\n",
      "decoder_layer2_att2 (64, 8, 31, 26)\n",
      "decoder_layer1_att1 (64, 8, 31, 31)\n"
     ]
    }
   ],
   "source": [
    "class Transformer(keras.Model):\n",
    "    def __init__(self, num_layers, input_vocab_size, target_vocab_size,\n",
    "                 max_length, d_model, num_heads, dff, rate=0.1):\n",
    "        super(Transformer, self).__init__()\n",
    "        \n",
    "        self.encoder_model = EncoderModel(\n",
    "            num_layers, input_vocab_size, max_length,\n",
    "            d_model, num_heads, dff, rate)\n",
    "        \n",
    "        self.decoder_model = DecoderModel(\n",
    "            num_layers, target_vocab_size, max_length,\n",
    "            d_model, num_heads, dff, rate)\n",
    "        \n",
    "        self.final_layer = keras.layers.Dense(target_vocab_size)\n",
    "    \n",
    "    def call(self, inp, tar, training, encoder_padding_mask,\n",
    "             decoder_mask, encoder_decoder_padding_mask):\n",
    "        # encoding_outputs.shape: (batch_size, input_seq_len, d_model)\n",
    "        encoding_outputs = self.encoder_model(\n",
    "            inp, training, encoder_padding_mask)\n",
    "        \n",
    "        # decoding_outputs.shape: (batch_size, output_seq_len, d_model)\n",
    "        decoding_outputs, attention_weights = self.decoder_model(\n",
    "            tar, encoding_outputs, training,\n",
    "            decoder_mask, encoder_decoder_padding_mask)\n",
    "        \n",
    "        # predictions.shape: (batch_size, output_seq_len, target_vocab_size)\n",
    "        predictions = self.final_layer(decoding_outputs)\n",
    "        \n",
    "        return predictions, attention_weights\n",
    "    \n",
    "sample_transformer = Transformer(2, 8500, 8000, max_length,\n",
    "                                 512, 8, 2048, rate = 0.1)\n",
    "temp_input = tf.random.uniform((64, 26))\n",
    "temp_target = tf.random.uniform((64, 31))\n",
    "\n",
    "predictions, attention_weights = sample_transformer(\n",
    "    temp_input, temp_target, training = False,\n",
    "    encoder_padding_mask = None,\n",
    "    decoder_mask = None,\n",
    "    encoder_decoder_padding_mask = None)\n",
    "\n",
    "print(predictions.shape)\n",
    "for key in attention_weights:\n",
    "    print(key, attention_weights[key].shape)\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1. initializes model.\n",
    "# 2. define loss, optimizer, learning_rate schedule\n",
    "# 3. train_step\n",
    "# 4. train process"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_layers = 4\n",
    "d_model = 128\n",
    "dff = 512\n",
    "num_heads = 8\n",
    "\n",
    "input_vocab_size = pt_tokenizer.vocab_size + 2\n",
    "target_vocab_size = en_tokenizer.vocab_size + 2\n",
    "\n",
    "dropout_rate = 0.1\n",
    "\n",
    "transformer = Transformer(num_layers,\n",
    "                          input_vocab_size,\n",
    "                          target_vocab_size,\n",
    "                          max_length,\n",
    "                          d_model, num_heads, dff, dropout_rate)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "# lrate = (d_model ** -0.5) * min(step_num ** (-0.5),\n",
    "#                                 step_num * warm_up_steps **(-1.5))\n",
    "\n",
    "class CustomizedSchedule(\n",
    "    keras.optimizers.schedules.LearningRateSchedule):\n",
    "    def __init__(self, d_model, warmup_steps = 4000):\n",
    "        super(CustomizedSchedule, self).__init__()\n",
    "        \n",
    "        self.d_model = tf.cast(d_model, tf.float32)\n",
    "        self.warmup_steps = warmup_steps\n",
    "    \n",
    "    def __call__(self, step):\n",
    "        arg1 = tf.math.rsqrt(step)\n",
    "        arg2 = step * (self.warmup_steps ** (-1.5))\n",
    "        \n",
    "        arg3 = tf.math.rsqrt(self.d_model)\n",
    "        \n",
    "        return arg3 * tf.math.minimum(arg1, arg2)\n",
    "    \n",
    "learning_rate = CustomizedSchedule(d_model)\n",
    "optimizer = keras.optimizers.Adam(learning_rate,\n",
    "                                  beta_1 = 0.9,\n",
    "                                  beta_2 = 0.98,\n",
    "                                  epsilon = 1e-9)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 0, 'Train step')"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "temp_learning_rate_schedule = CustomizedSchedule(d_model)\n",
    "\n",
    "plt.plot(\n",
    "    temp_learning_rate_schedule(\n",
    "        tf.range(40000, dtype=tf.float32)))\n",
    "plt.ylabel(\"Leraning rate\")\n",
    "plt.xlabel(\"Train step\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "loss_object = keras.losses.SparseCategoricalCrossentropy(\n",
    "    from_logits = True, reduction = 'none')\n",
    "\n",
    "def loss_function(real, pred):\n",
    "    mask = tf.math.logical_not(tf.math.equal(real, 0))\n",
    "    loss_ = loss_object(real, pred)\n",
    "    \n",
    "    mask = tf.cast(mask, dtype=loss_.dtype)\n",
    "    loss_ *= mask\n",
    "    \n",
    "    return tf.reduce_mean(loss_)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_masks(inp, tar):\n",
    "    \"\"\"\n",
    "    Encoder:\n",
    "      - encoder_padding_mask (self attention of EncoderLayer)\n",
    "    Decoder:\n",
    "      - look_ahead_mask (self attention of DecoderLayer)\n",
    "      - encoder_decoder_padding_mask (encoder-decoder attention of DecoderLayer)\n",
    "      - decoder_padding_mask (self attention of DecoderLayer)\n",
    "    \"\"\"\n",
    "    encoder_padding_mask = create_padding_mask(inp)\n",
    "    encoder_decoder_padding_mask = create_padding_mask(inp)\n",
    "    \n",
    "    look_ahead_mask = create_look_ahead_mask(tf.shape(tar)[1])\n",
    "    decoder_padding_mask = create_padding_mask(tar)\n",
    "    decoder_mask = tf.maximum(decoder_padding_mask,\n",
    "                              look_ahead_mask)\n",
    "    \n",
    "    return encoder_padding_mask, decoder_mask, encoder_decoder_padding_mask"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "temp_inp, temp_tar = iter(train_dataset.take(1)).next()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(64, 38)\n",
      "(64, 38)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(<tf.Tensor: id=456583, shape=(64, 1, 1, 38), dtype=float32, numpy=\n",
       " array([[[[0., 0., 0., ..., 1., 1., 1.]]],\n",
       " \n",
       " \n",
       "        [[[0., 0., 0., ..., 1., 1., 1.]]],\n",
       " \n",
       " \n",
       "        [[[0., 0., 0., ..., 1., 1., 1.]]],\n",
       " \n",
       " \n",
       "        ...,\n",
       " \n",
       " \n",
       "        [[[0., 0., 0., ..., 1., 1., 1.]]],\n",
       " \n",
       " \n",
       "        [[[0., 0., 0., ..., 1., 1., 1.]]],\n",
       " \n",
       " \n",
       "        [[[0., 0., 0., ..., 1., 1., 1.]]]], dtype=float32)>,\n",
       " <tf.Tensor: id=456615, shape=(64, 1, 38, 38), dtype=float32, numpy=\n",
       " array([[[[0., 1., 1., ..., 1., 1., 1.],\n",
       "          [0., 0., 1., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          ...,\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.]]],\n",
       " \n",
       " \n",
       "        [[[0., 1., 1., ..., 1., 1., 1.],\n",
       "          [0., 0., 1., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          ...,\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.]]],\n",
       " \n",
       " \n",
       "        [[[0., 1., 1., ..., 1., 1., 1.],\n",
       "          [0., 0., 1., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          ...,\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.]]],\n",
       " \n",
       " \n",
       "        ...,\n",
       " \n",
       " \n",
       "        [[[0., 1., 1., ..., 1., 1., 1.],\n",
       "          [0., 0., 1., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          ...,\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.]]],\n",
       " \n",
       " \n",
       "        [[[0., 1., 1., ..., 1., 1., 1.],\n",
       "          [0., 0., 1., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          ...,\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.]]],\n",
       " \n",
       " \n",
       "        [[[0., 1., 1., ..., 1., 1., 1.],\n",
       "          [0., 0., 1., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          ...,\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.],\n",
       "          [0., 0., 0., ..., 1., 1., 1.]]]], dtype=float32)>,\n",
       " <tf.Tensor: id=456590, shape=(64, 1, 1, 38), dtype=float32, numpy=\n",
       " array([[[[0., 0., 0., ..., 1., 1., 1.]]],\n",
       " \n",
       " \n",
       "        [[[0., 0., 0., ..., 1., 1., 1.]]],\n",
       " \n",
       " \n",
       "        [[[0., 0., 0., ..., 1., 1., 1.]]],\n",
       " \n",
       " \n",
       "        ...,\n",
       " \n",
       " \n",
       "        [[[0., 0., 0., ..., 1., 1., 1.]]],\n",
       " \n",
       " \n",
       "        [[[0., 0., 0., ..., 1., 1., 1.]]],\n",
       " \n",
       " \n",
       "        [[[0., 0., 0., ..., 1., 1., 1.]]]], dtype=float32)>)"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "print(temp_inp.shape)\n",
    "print(temp_tar.shape)\n",
    "create_masks(temp_inp, temp_tar)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1 Batch 0 Loss 4.1286 Accuracy 0.0013\n",
      "Epoch 1 Batch 100 Loss 4.1847 Accuracy 0.0133\n",
      "Epoch 1 Batch 200 Loss 4.0660 Accuracy 0.0200\n",
      "Epoch 1 Batch 300 Loss 3.9233 Accuracy 0.0236\n",
      "Epoch 1 Batch 400 Loss 3.7440 Accuracy 0.0310\n",
      "Epoch 1 Batch 500 Loss 3.5987 Accuracy 0.0376\n",
      "Epoch 1 Batch 600 Loss 3.4758 Accuracy 0.0443\n",
      "Epoch 1 Batch 700 Loss 3.3677 Accuracy 0.0512\n",
      "Epoch 1 Loss 3.3654 Accuracy 0.0513\n",
      "Time take for 1 epoch: 1228.2845702171326 secs\n",
      "\n",
      "Epoch 2 Batch 0 Loss 2.5084 Accuracy 0.0984\n",
      "Epoch 2 Batch 100 Loss 2.5704 Accuracy 0.1066\n",
      "Epoch 2 Batch 200 Loss 2.5166 Accuracy 0.1106\n",
      "Epoch 2 Batch 300 Loss 2.4856 Accuracy 0.1144\n",
      "Epoch 2 Batch 400 Loss 2.4452 Accuracy 0.1175\n",
      "Epoch 2 Batch 500 Loss 2.4168 Accuracy 0.1205\n",
      "Epoch 2 Batch 600 Loss 2.3900 Accuracy 0.1230\n",
      "Epoch 2 Batch 700 Loss 2.3664 Accuracy 0.1254\n",
      "Epoch 2 Loss 2.3657 Accuracy 0.1255\n",
      "Time take for 1 epoch: 83.50327587127686 secs\n",
      "\n",
      "Epoch 3 Batch 0 Loss 2.0950 Accuracy 0.1334\n",
      "Epoch 3 Batch 100 Loss 2.1885 Accuracy 0.1424\n",
      "Epoch 3 Batch 200 Loss 2.1701 Accuracy 0.1437\n",
      "Epoch 3 Batch 300 Loss 2.1650 Accuracy 0.1456\n",
      "Epoch 3 Batch 400 Loss 2.1458 Accuracy 0.1467\n",
      "Epoch 3 Batch 500 Loss 2.1334 Accuracy 0.1482\n",
      "Epoch 3 Batch 600 Loss 2.1182 Accuracy 0.1497\n",
      "Epoch 3 Batch 700 Loss 2.1033 Accuracy 0.1515\n",
      "Epoch 3 Loss 2.1028 Accuracy 0.1516\n",
      "Time take for 1 epoch: 81.84765362739563 secs\n",
      "\n",
      "Epoch 4 Batch 0 Loss 1.9093 Accuracy 0.1592\n",
      "Epoch 4 Batch 100 Loss 1.9803 Accuracy 0.1648\n",
      "Epoch 4 Batch 200 Loss 1.9607 Accuracy 0.1663\n",
      "Epoch 4 Batch 300 Loss 1.9500 Accuracy 0.1691\n",
      "Epoch 4 Batch 400 Loss 1.9259 Accuracy 0.1715\n",
      "Epoch 4 Batch 500 Loss 1.9089 Accuracy 0.1740\n",
      "Epoch 4 Batch 600 Loss 1.8914 Accuracy 0.1761\n",
      "Epoch 4 Batch 700 Loss 1.8739 Accuracy 0.1784\n",
      "Epoch 4 Loss 1.8734 Accuracy 0.1784\n",
      "Time take for 1 epoch: 83.20685529708862 secs\n",
      "\n",
      "Epoch 5 Batch 0 Loss 1.7084 Accuracy 0.1883\n",
      "Epoch 5 Batch 100 Loss 1.7365 Accuracy 0.1952\n",
      "Epoch 5 Batch 200 Loss 1.7194 Accuracy 0.1966\n",
      "Epoch 5 Batch 300 Loss 1.7115 Accuracy 0.1990\n",
      "Epoch 5 Batch 400 Loss 1.6924 Accuracy 0.2005\n",
      "Epoch 5 Batch 500 Loss 1.6795 Accuracy 0.2023\n",
      "Epoch 5 Batch 600 Loss 1.6653 Accuracy 0.2038\n",
      "Epoch 5 Batch 700 Loss 1.6511 Accuracy 0.2056\n",
      "Epoch 5 Loss 1.6507 Accuracy 0.2056\n",
      "Time take for 1 epoch: 81.60697937011719 secs\n",
      "\n",
      "Epoch 6 Batch 0 Loss 1.5371 Accuracy 0.2010\n",
      "Epoch 6 Batch 100 Loss 1.5403 Accuracy 0.2175\n",
      "Epoch 6 Batch 200 Loss 1.5252 Accuracy 0.2178\n",
      "Epoch 6 Batch 300 Loss 1.5194 Accuracy 0.2196\n",
      "Epoch 6 Batch 400 Loss 1.5024 Accuracy 0.2210\n",
      "Epoch 6 Batch 500 Loss 1.4928 Accuracy 0.2224\n",
      "Epoch 6 Batch 600 Loss 1.4806 Accuracy 0.2236\n",
      "Epoch 6 Batch 700 Loss 1.4674 Accuracy 0.2252\n",
      "Epoch 6 Loss 1.4671 Accuracy 0.2252\n",
      "Time take for 1 epoch: 82.22119450569153 secs\n",
      "\n",
      "Epoch 7 Batch 0 Loss 1.3790 Accuracy 0.2149\n",
      "Epoch 7 Batch 100 Loss 1.3642 Accuracy 0.2358\n",
      "Epoch 7 Batch 200 Loss 1.3469 Accuracy 0.2372\n",
      "Epoch 7 Batch 300 Loss 1.3404 Accuracy 0.2396\n",
      "Epoch 7 Batch 400 Loss 1.3233 Accuracy 0.2409\n",
      "Epoch 7 Batch 500 Loss 1.3130 Accuracy 0.2424\n",
      "Epoch 7 Batch 600 Loss 1.3010 Accuracy 0.2437\n",
      "Epoch 7 Batch 700 Loss 1.2886 Accuracy 0.2454\n",
      "Epoch 7 Loss 1.2882 Accuracy 0.2454\n",
      "Time take for 1 epoch: 81.69875907897949 secs\n",
      "\n",
      "Epoch 8 Batch 0 Loss 1.2083 Accuracy 0.2361\n",
      "Epoch 8 Batch 100 Loss 1.1888 Accuracy 0.2565\n",
      "Epoch 8 Batch 200 Loss 1.1780 Accuracy 0.2572\n",
      "Epoch 8 Batch 300 Loss 1.1745 Accuracy 0.2591\n",
      "Epoch 8 Batch 400 Loss 1.1612 Accuracy 0.2601\n",
      "Epoch 8 Batch 500 Loss 1.1536 Accuracy 0.2613\n",
      "Epoch 8 Batch 600 Loss 1.1453 Accuracy 0.2623\n",
      "Epoch 8 Batch 700 Loss 1.1366 Accuracy 0.2636\n",
      "Epoch 8 Loss 1.1363 Accuracy 0.2637\n",
      "Time take for 1 epoch: 81.40631866455078 secs\n",
      "\n",
      "Epoch 9 Batch 0 Loss 1.0894 Accuracy 0.2479\n",
      "Epoch 9 Batch 100 Loss 1.0688 Accuracy 0.2720\n",
      "Epoch 9 Batch 200 Loss 1.0573 Accuracy 0.2724\n",
      "Epoch 9 Batch 300 Loss 1.0564 Accuracy 0.2743\n",
      "Epoch 9 Batch 400 Loss 1.0452 Accuracy 0.2750\n",
      "Epoch 9 Batch 500 Loss 1.0406 Accuracy 0.2757\n",
      "Epoch 9 Batch 600 Loss 1.0343 Accuracy 0.2765\n",
      "Epoch 9 Batch 700 Loss 1.0281 Accuracy 0.2774\n",
      "Epoch 9 Loss 1.0278 Accuracy 0.2774\n",
      "Time take for 1 epoch: 81.66021227836609 secs\n",
      "\n",
      "Epoch 10 Batch 0 Loss 1.0079 Accuracy 0.2559\n",
      "Epoch 10 Batch 100 Loss 0.9726 Accuracy 0.2842\n",
      "Epoch 10 Batch 200 Loss 0.9645 Accuracy 0.2846\n",
      "Epoch 10 Batch 300 Loss 0.9653 Accuracy 0.2860\n",
      "Epoch 10 Batch 400 Loss 0.9574 Accuracy 0.2864\n",
      "Epoch 10 Batch 500 Loss 0.9550 Accuracy 0.2867\n",
      "Epoch 10 Batch 600 Loss 0.9502 Accuracy 0.2871\n",
      "Epoch 10 Batch 700 Loss 0.9447 Accuracy 0.2881\n",
      "Epoch 10 Loss 0.9444 Accuracy 0.2881\n",
      "Time take for 1 epoch: 84.60214877128601 secs\n",
      "\n",
      "Epoch 11 Batch 0 Loss 0.9380 Accuracy 0.2690\n",
      "Epoch 11 Batch 100 Loss 0.9021 Accuracy 0.2930\n",
      "Epoch 11 Batch 200 Loss 0.8918 Accuracy 0.2939\n",
      "Epoch 11 Batch 300 Loss 0.8942 Accuracy 0.2949\n",
      "Epoch 11 Batch 400 Loss 0.8872 Accuracy 0.2953\n",
      "Epoch 11 Batch 500 Loss 0.8840 Accuracy 0.2960\n",
      "Epoch 11 Batch 600 Loss 0.8793 Accuracy 0.2964\n",
      "Epoch 11 Batch 700 Loss 0.8750 Accuracy 0.2973\n",
      "Epoch 11 Loss 0.8747 Accuracy 0.2973\n",
      "Time take for 1 epoch: 81.51005268096924 secs\n",
      "\n",
      "Epoch 12 Batch 0 Loss 0.8958 Accuracy 0.2736\n",
      "Epoch 12 Batch 100 Loss 0.8434 Accuracy 0.3007\n",
      "Epoch 12 Batch 200 Loss 0.8368 Accuracy 0.3006\n",
      "Epoch 12 Batch 300 Loss 0.8378 Accuracy 0.3021\n",
      "Epoch 12 Batch 400 Loss 0.8314 Accuracy 0.3024\n",
      "Epoch 12 Batch 500 Loss 0.8294 Accuracy 0.3028\n",
      "Epoch 12 Batch 600 Loss 0.8250 Accuracy 0.3034\n",
      "Epoch 12 Batch 700 Loss 0.8215 Accuracy 0.3041\n",
      "Epoch 12 Loss 0.8213 Accuracy 0.3041\n",
      "Time take for 1 epoch: 82.57502746582031 secs\n",
      "\n",
      "Epoch 13 Batch 0 Loss 0.8375 Accuracy 0.2821\n",
      "Epoch 13 Batch 100 Loss 0.7892 Accuracy 0.3088\n",
      "Epoch 13 Batch 200 Loss 0.7872 Accuracy 0.3078\n",
      "Epoch 13 Batch 300 Loss 0.7880 Accuracy 0.3094\n",
      "Epoch 13 Batch 400 Loss 0.7822 Accuracy 0.3095\n",
      "Epoch 13 Batch 500 Loss 0.7803 Accuracy 0.3100\n",
      "Epoch 13 Batch 600 Loss 0.7772 Accuracy 0.3104\n",
      "Epoch 13 Batch 700 Loss 0.7742 Accuracy 0.3111\n",
      "Epoch 13 Loss 0.7739 Accuracy 0.3111\n",
      "Time take for 1 epoch: 82.3340437412262 secs\n",
      "\n",
      "Epoch 14 Batch 0 Loss 0.7804 Accuracy 0.2914\n",
      "Epoch 14 Batch 100 Loss 0.7471 Accuracy 0.3145\n",
      "Epoch 14 Batch 200 Loss 0.7420 Accuracy 0.3141\n",
      "Epoch 14 Batch 300 Loss 0.7432 Accuracy 0.3157\n",
      "Epoch 14 Batch 400 Loss 0.7388 Accuracy 0.3158\n",
      "Epoch 14 Batch 500 Loss 0.7368 Accuracy 0.3161\n",
      "Epoch 14 Batch 600 Loss 0.7343 Accuracy 0.3163\n",
      "Epoch 14 Batch 700 Loss 0.7315 Accuracy 0.3169\n",
      "Epoch 14 Loss 0.7312 Accuracy 0.3169\n",
      "Time take for 1 epoch: 82.88241934776306 secs\n",
      "\n",
      "Epoch 15 Batch 0 Loss 0.7507 Accuracy 0.2965\n",
      "Epoch 15 Batch 100 Loss 0.7109 Accuracy 0.3194\n",
      "Epoch 15 Batch 200 Loss 0.7032 Accuracy 0.3200\n",
      "Epoch 15 Batch 300 Loss 0.7058 Accuracy 0.3210\n",
      "Epoch 15 Batch 400 Loss 0.7010 Accuracy 0.3212\n",
      "Epoch 15 Batch 500 Loss 0.6994 Accuracy 0.3216\n",
      "Epoch 15 Batch 600 Loss 0.6981 Accuracy 0.3218\n",
      "Epoch 15 Batch 700 Loss 0.6957 Accuracy 0.3223\n",
      "Epoch 15 Loss 0.6954 Accuracy 0.3223\n",
      "Time take for 1 epoch: 82.09047102928162 secs\n",
      "\n",
      "Epoch 16 Batch 0 Loss 0.7195 Accuracy 0.2977\n",
      "Epoch 16 Batch 100 Loss 0.6772 Accuracy 0.3246\n",
      "Epoch 16 Batch 200 Loss 0.6709 Accuracy 0.3248\n",
      "Epoch 16 Batch 300 Loss 0.6739 Accuracy 0.3258\n",
      "Epoch 16 Batch 400 Loss 0.6696 Accuracy 0.3258\n",
      "Epoch 16 Batch 500 Loss 0.6682 Accuracy 0.3263\n",
      "Epoch 16 Batch 600 Loss 0.6683 Accuracy 0.3260\n",
      "Epoch 16 Batch 700 Loss 0.6661 Accuracy 0.3265\n",
      "Epoch 16 Loss 0.6658 Accuracy 0.3265\n",
      "Time take for 1 epoch: 81.26668906211853 secs\n",
      "\n",
      "Epoch 17 Batch 0 Loss 0.6785 Accuracy 0.3049\n",
      "Epoch 17 Batch 100 Loss 0.6510 Accuracy 0.3283\n",
      "Epoch 17 Batch 200 Loss 0.6437 Accuracy 0.3289\n",
      "Epoch 17 Batch 300 Loss 0.6470 Accuracy 0.3298\n",
      "Epoch 17 Batch 400 Loss 0.6424 Accuracy 0.3299\n",
      "Epoch 17 Batch 500 Loss 0.6413 Accuracy 0.3302\n",
      "Epoch 17 Batch 600 Loss 0.6404 Accuracy 0.3303\n",
      "Epoch 17 Batch 700 Loss 0.6382 Accuracy 0.3309\n",
      "Epoch 17 Loss 0.6379 Accuracy 0.3309\n",
      "Time take for 1 epoch: 81.91204142570496 secs\n",
      "\n",
      "Epoch 18 Batch 0 Loss 0.6537 Accuracy 0.3045\n",
      "Epoch 18 Batch 100 Loss 0.6246 Accuracy 0.3323\n",
      "Epoch 18 Batch 200 Loss 0.6178 Accuracy 0.3328\n",
      "Epoch 18 Batch 300 Loss 0.6207 Accuracy 0.3341\n",
      "Epoch 18 Batch 400 Loss 0.6171 Accuracy 0.3340\n",
      "Epoch 18 Batch 500 Loss 0.6164 Accuracy 0.3341\n",
      "Epoch 18 Batch 600 Loss 0.6161 Accuracy 0.3340\n",
      "Epoch 18 Batch 700 Loss 0.6144 Accuracy 0.3346\n",
      "Epoch 18 Loss 0.6142 Accuracy 0.3346\n",
      "Time take for 1 epoch: 81.26650643348694 secs\n",
      "\n",
      "Epoch 19 Batch 0 Loss 0.6470 Accuracy 0.3079\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 19 Batch 100 Loss 0.6003 Accuracy 0.3367\n",
      "Epoch 19 Batch 200 Loss 0.5938 Accuracy 0.3368\n",
      "Epoch 19 Batch 300 Loss 0.5967 Accuracy 0.3376\n",
      "Epoch 19 Batch 400 Loss 0.5941 Accuracy 0.3374\n",
      "Epoch 19 Batch 500 Loss 0.5945 Accuracy 0.3375\n",
      "Epoch 19 Batch 600 Loss 0.5944 Accuracy 0.3374\n",
      "Epoch 19 Batch 700 Loss 0.5927 Accuracy 0.3379\n",
      "Epoch 19 Loss 0.5925 Accuracy 0.3379\n",
      "Time take for 1 epoch: 81.16860604286194 secs\n",
      "\n",
      "Epoch 20 Batch 0 Loss 0.5969 Accuracy 0.3201\n",
      "Epoch 20 Batch 100 Loss 0.5806 Accuracy 0.3391\n",
      "Epoch 20 Batch 200 Loss 0.5727 Accuracy 0.3396\n",
      "Epoch 20 Batch 300 Loss 0.5745 Accuracy 0.3411\n",
      "Epoch 20 Batch 400 Loss 0.5710 Accuracy 0.3409\n",
      "Epoch 20 Batch 500 Loss 0.5711 Accuracy 0.3411\n",
      "Epoch 20 Batch 600 Loss 0.5708 Accuracy 0.3411\n",
      "Epoch 20 Batch 700 Loss 0.5698 Accuracy 0.3415\n",
      "Epoch 20 Loss 0.5696 Accuracy 0.3415\n",
      "Time take for 1 epoch: 80.9169328212738 secs\n",
      "\n"
     ]
    }
   ],
   "source": [
    "train_loss = keras.metrics.Mean(name = 'train_loss')\n",
    "train_accuracy = keras.metrics.SparseCategoricalAccuracy(\n",
    "    name = 'train_accuracy')\n",
    "\n",
    "@tf.function\n",
    "def train_step(inp, tar):\n",
    "    tar_inp  = tar[:, :-1]\n",
    "    tar_real = tar[:, 1:]\n",
    "    \n",
    "    encoder_padding_mask, decoder_mask, encoder_decoder_padding_mask \\\n",
    "    = create_masks(inp, tar_inp)\n",
    "    \n",
    "    with tf.GradientTape() as tape:\n",
    "        predictions, _ = transformer(inp, tar_inp, True,\n",
    "                                     encoder_padding_mask,\n",
    "                                     decoder_mask,\n",
    "                                     encoder_decoder_padding_mask)\n",
    "        loss = loss_function(tar_real, predictions)\n",
    "    \n",
    "    gradients = tape.gradient(loss, transformer.trainable_variables)\n",
    "    optimizer.apply_gradients(\n",
    "        zip(gradients, transformer.trainable_variables))\n",
    "    train_loss(loss)\n",
    "    train_accuracy(tar_real, predictions)\n",
    "\n",
    "epochs = 20\n",
    "for epoch in range(epochs):\n",
    "    start = time.time()\n",
    "    train_loss.reset_states()\n",
    "    train_accuracy.reset_states()\n",
    "    \n",
    "    for (batch, (inp, tar)) in enumerate(train_dataset):\n",
    "        train_step(inp, tar)\n",
    "        if batch % 100 == 0:\n",
    "            print('Epoch {} Batch {} Loss {:.4f} Accuracy {:.4f}'.format(\n",
    "                epoch + 1, batch, train_loss.result(),\n",
    "                train_accuracy.result()))\n",
    "    \n",
    "    print('Epoch {} Loss {:.4f} Accuracy {:.4f}'.format(\n",
    "        epoch + 1, train_loss.result(), train_accuracy.result()))\n",
    "    print('Time take for 1 epoch: {} secs\\n'.format(\n",
    "        time.time() - start))\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "eg: A B C D -> E F G H.\n",
    "Train: A B C D, E F G -> F G H\n",
    "Eval:  A B C D -> E\n",
    "       A B C D, E -> F\n",
    "       A B C D, E F -> G\n",
    "       A B C D, E F G -> H\n",
    "\"\"\"\n",
    "def evaluate(inp_sentence):\n",
    "    input_id_sentence = [pt_tokenizer.vocab_size] \\\n",
    "    + pt_tokenizer.encode(inp_sentence) + [pt_tokenizer.vocab_size + 1]\n",
    "    # encoder_input.shape: (1, input_sentence_length)\n",
    "    encoder_input = tf.expand_dims(input_id_sentence, 0)\n",
    "    \n",
    "    # decoder_input.shape: (1, 1)\n",
    "    decoder_input = tf.expand_dims([en_tokenizer.vocab_size], 0)\n",
    "    \n",
    "    for i in range(max_length):\n",
    "        encoder_padding_mask, decoder_mask, encoder_decoder_padding_mask \\\n",
    "        = create_masks(encoder_input, decoder_input)\n",
    "        # predictions.shape: (batch_size, output_target_len, target_vocab_size)\n",
    "        predictions, attention_weights = transformer(\n",
    "            encoder_input,\n",
    "            decoder_input,\n",
    "            False,\n",
    "            encoder_padding_mask,\n",
    "            decoder_mask,\n",
    "            encoder_decoder_padding_mask)\n",
    "        # predictions.shape: (batch_size, target_vocab_size)\n",
    "        predictions = predictions[:, -1, :]\n",
    "        \n",
    "        predicted_id = tf.cast(tf.argmax(predictions, axis = -1),\n",
    "                               tf.int32)\n",
    "        \n",
    "        if tf.equal(predicted_id, en_tokenizer.vocab_size + 1):\n",
    "            return tf.squeeze(decoder_input, axis = 0), attention_weights\n",
    "        \n",
    "        decoder_input = tf.concat([decoder_input, [predicted_id]],\n",
    "                                  axis = -1)\n",
    "    return tf.squeeze(decoder_input, axis = 0), attention_weights\n",
    "        \n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_encoder_decoder_attention(attention, input_sentence,\n",
    "                                   result, layer_name):\n",
    "    fig = plt.figure(figsize = (16, 8))\n",
    "    \n",
    "    input_id_sentence = pt_tokenizer.encode(input_sentence)\n",
    "    \n",
    "    # attention.shape: (num_heads, tar_len, input_len)\n",
    "    attention = tf.squeeze(attention[layer_name], axis = 0)\n",
    "    \n",
    "    for head in range(attention.shape[0]):\n",
    "        ax = fig.add_subplot(2, 4, head + 1)\n",
    "        \n",
    "        ax.matshow(attention[head][:-1, :])\n",
    "        \n",
    "        fontdict = {'fontsize': 10}\n",
    "        \n",
    "        ax.set_xticks(range(len(input_id_sentence) + 2))\n",
    "        ax.set_yticks(range(len(result)))\n",
    "        \n",
    "        ax.set_ylim(len(result) - 1.5, -0.5)\n",
    "        \n",
    "        ax.set_xticklabels(\n",
    "            ['<start>'] + [pt_tokenizer.decode([i]) for i in input_id_sentence] + ['<end>'],\n",
    "            fontdict = fontdict, rotation = 90)\n",
    "        ax.set_yticklabels(\n",
    "            [en_tokenizer.decode([i]) for i in result if i < en_tokenizer.vocab_size],\n",
    "            fontdict = fontdict)\n",
    "        ax.set_xlabel('Head {}'.format(head + 1))\n",
    "    plt.tight_layout()\n",
    "    plt.show()      "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [],
   "source": [
    "def translate(input_sentence, layer_name = ''):\n",
    "    result, attention_weights = evaluate(input_sentence)\n",
    "    \n",
    "    predicted_sentence = en_tokenizer.decode(\n",
    "        [i for i in result if i < en_tokenizer.vocab_size])\n",
    "    \n",
    "    print(\"Input: {}\".format(input_sentence))\n",
    "    print(\"Predicted translation: {}\".format(predicted_sentence))\n",
    "    \n",
    "    if layer_name:\n",
    "        plot_encoder_decoder_attention(attention_weights, input_sentence,\n",
    "                                       result, layer_name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input: está muito frio aqui.\n",
      "Predicted translation: it 's very cold here .\n"
     ]
    }
   ],
   "source": [
    "translate('está muito frio aqui.')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input: isto é minha vida\n",
      "Predicted translation: this is my life-altering .\n"
     ]
    }
   ],
   "source": [
    "translate('isto é minha vida')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input: você ainda está em casa?\n",
      "Predicted translation: are you still home ?\n"
     ]
    }
   ],
   "source": [
    "translate('você ainda está em casa?')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input: este é o primeiro livro que eu já li\n",
      "Predicted translation: this is the first book that i already have .\n"
     ]
    }
   ],
   "source": [
    "translate('este é o primeiro livro que eu já li')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input: este é o primeiro livro que eu já li\n",
      "Predicted translation: this is the first book that i already have .\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1152x576 with 8 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "translate('este é o primeiro livro que eu já li',\n",
    "          layer_name = 'decoder_layer4_att2')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
